package common.kubernetes.tiller;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.net.URLConnection;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Future;

import org.apache.commons.lang3.Validate;
import org.microbean.helm.ReleaseManager;
import org.microbean.helm.Tiller;
import org.microbean.helm.chart.DirectoryChartLoader;
import org.microbean.helm.chart.URLChartLoader;
import org.yaml.snakeyaml.Yaml;

import com.alibaba.fastjson.JSONObject;

import common.kubernetes.apiserver.KubeClient;
import common.kubernetes.constant.ResourcesType;
import hapi.chart.ChartOuterClass.Chart;
import hapi.release.ReleaseOuterClass.Release;
import hapi.release.StatusOuterClass.Status;
import hapi.services.tiller.ReleaseServiceGrpc.ReleaseServiceBlockingStub;
import hapi.services.tiller.Tiller.GetReleaseStatusRequest;
import hapi.services.tiller.Tiller.GetReleaseStatusResponse;
import hapi.services.tiller.Tiller.InstallReleaseRequest;
import hapi.services.tiller.Tiller.InstallReleaseResponse;
import hapi.services.tiller.Tiller.UninstallReleaseRequest;
import hapi.services.tiller.Tiller.UninstallReleaseResponse;
import io.fabric8.kubernetes.client.DefaultKubernetesClient;
import io.grpc.ManagedChannel;
import io.grpc.StatusRuntimeException;
import io.grpc.netty.NegotiationType;
import io.grpc.netty.NettyChannelBuilder;
import lombok.extern.slf4j.Slf4j;
import net.wicp.tams.common.Conf;
import net.wicp.tams.common.Result;
import net.wicp.tams.common.apiext.CollectionUtil;
import net.wicp.tams.common.apiext.StringUtil;
import net.wicp.tams.common.apiext.json.JSONUtil;

@Slf4j
public class TillerClient {
	private static TillerClient tillerClient = null;
	private final Tiller tiller;

	public Tiller getTiller() {
		return tiller;
	}

	private final DirectoryChartLoader load = new DirectoryChartLoader();

	private final URLChartLoader urlLoader = new URLChartLoader();

	// 关闭会报“rejected from
	// java.util.concurrent.ThreadPoolExecutor@77979a6c[Terminated, pool size =
	// 0。。。。”
	// 用它一段时候后堵塞
	// private final ReleaseManager chartManager;

	private TillerClient() {
		if (StringUtil.isNotNull(Conf.get("common.kubernetes.tiller.serverip"))) {
			ManagedChannel channel = NettyChannelBuilder
					.forAddress(Conf.get("common.kubernetes.tiller.serverip"),
							Conf.getInt("common.kubernetes.tiller.port"))
					.negotiationType(NegotiationType.PLAINTEXT).build();

			this.tiller = new Tiller(channel);
			/*
			 * this.blockingStub =
			 * ReleaseServiceGrpc.newBlockingStub(channel).withCallCredentials(new
			 * CallCredentials() {
			 * 
			 * @Override public void applyRequestMetadata(MethodDescriptor<?, ?> method,
			 * Attributes attrs, java.util.concurrent.Executor appExecutor, MetadataApplier
			 * applier) { applier.apply(metadata); }
			 * 
			 * @Override public void thisUsesUnstableApi() {
			 * 
			 * } }); futureStub =
			 * MetadataUtils.attachHeaders(ReleaseServiceGrpc.newFutureStub(channel),
			 * metadata);
			 */
		} else {
			// TODO 经常性报
			// [PortForwarderWebsocket.java:243] [msg=]- Error while forwarding data to the
			// client java.io.IOException: 您的主机中的软件中止了一个已建立的连接。
			DefaultKubernetesClient client = KubeClient.getClient();
			try {
				this.tiller = new Tiller(client);
			} catch (MalformedURLException e) {
				log.error("error", e);
				throw new RuntimeException(e.getMessage());
			}
		}
	}

	public static TillerClient getInst() {
		if (TillerClient.tillerClient == null) {
			synchronized (TillerClient.class) {
				if (TillerClient.tillerClient == null) {
					TillerClient.tillerClient = new TillerClient();
				}
			}
		}
		return TillerClient.tillerClient;
	}

	public void shutdown() {
		try {
			this.tiller.close();
			tillerClient = null;
		} catch (IOException e) {
			log.error("关闭连接失败", e);
		}
	}

	public ReleaseServiceBlockingStub getBlockingStub() {
		return this.tiller.getReleaseServiceBlockingStub();
	}

	// 覆盖values的值
	@SuppressWarnings("rawtypes")
	public static void setValue(InstallReleaseRequest.Builder requestBuilder, Object... keyValues) {
		Map newMap = CollectionUtil.newMap(keyValues);
		JSONObject obj = JSONUtil.getJsonForMapFlat(newMap);

		final String yamlString = new Yaml().dump(obj);
		// Set the user-supplied values in the only way that will be readable by
		// Tiller. For some reason, Tiller itself only ever looks at the return
		// value of Config.Builder#getRaw(), and no other values-related "getter"
		// method.
		requestBuilder.getValuesBuilder().setRaw(yamlString);
	}

	public Chart.Builder chartBuilder(String chartsDirPath) {
		try {
			Chart.Builder chartBuilder = load.load(Paths.get(chartsDirPath));
			return chartBuilder;
		} catch (Exception e) {
			throw new RuntimeException(e.getMessage());
		}
	}

	// name 会把“_”转为“-”
	public Result installChart(String name, String namespace, Chart.Builder chartBuilder, Object... keyValues) {
		final InstallReleaseRequest.Builder requestBuilder = InstallReleaseRequest.newBuilder();
		requestBuilder.setTimeout(300L);
		name = name.replace("_", "-");// release不允许出现"_"
		requestBuilder.setName(name);
		TillerClient.setValue(requestBuilder, keyValues);
		requestBuilder.setNamespace(namespace);
		// requestBuilder.setNamespace(Conf.get("common.kubernetes.apiserver.namespace.default"));
		requestBuilder.setWait(true);
		try {
			ReleaseManager chartManager = new ReleaseManager(this.tiller);
			final Future<InstallReleaseResponse> releaseFuture = chartManager.install(requestBuilder, chartBuilder);
			final Release release = releaseFuture.get().getRelease();
			// 关闭会报“rejected from
			// java.util.concurrent.ThreadPoolExecutor@77979a6c[Terminated, pool size =
			// 0。。。。”
			// chartManager.close();
			if (release != null && StringUtil.isNotNull(release.getName())) {
				return Result.getSuc();
			} else {
				return Result.getError("部署失败");
			}
		} catch (Exception e) {
			log.error("部署charts失败:", e);
			return Result.getError("部署失败:" + e.getMessage());
		}
	}

	public Result installDirChart(String name, String namespace, String chartsDirPath, Object... keyValues) {
		return installDirChart(name,namespace,chartsDirPath,"values.yaml",keyValues);
	}
	
	public Result installDirChart(String name, String namespace, String chartsDirPath, String valueFileName,Object... keyValues) {
		Chart.Builder chartBuilder = null;
		try {
			chartBuilder = load.load(Paths.get(chartsDirPath),valueFileName);
		} catch (Exception e) {
			log.error("目录【" + chartsDirPath + "】不能转为Chart，请检查正确的目录:", e);
			return Result.getError("目录【" + chartsDirPath + "】不能转为Chart，请检查正确的目录:" + e.getMessage());
		}
		return this.installChart(name, namespace, chartBuilder, keyValues);
	}
	

	public Result deleteChart(String name) {
		final UninstallReleaseRequest.Builder requestBuilder = UninstallReleaseRequest.newBuilder();
		requestBuilder.setTimeout(300L);
		name = name.replace("_", "-");// release不允许出现"_"
		requestBuilder.setName(name);
		requestBuilder.setPurge(true);
		try {
			ReleaseManager chartManager = new ReleaseManager(this.tiller);
			final Future<UninstallReleaseResponse> releaseFuture = chartManager.uninstall(requestBuilder.build());
			final UninstallReleaseResponse release = releaseFuture.get();
			if (release != null && release.getRelease().getInfo().getStatus().getCode() == Status.Code.DELETED) {
				return Result.getSuc();
			} else {
				return Result.getError("删除chart失败");
			}
		} catch (Exception e) {
			log.error("删除chart失败:", e);
			return Result.getError("部署失败:" + e.getMessage());
		}
	}

	public Map<ResourcesType, String> queryStatus(String releaseName) {
		try {
			final GetReleaseStatusRequest.Builder builder = GetReleaseStatusRequest.newBuilder();
			releaseName = releaseName.replace("_", "-");
			builder.setName(releaseName);
			ReleaseManager chartManager = new ReleaseManager(this.tiller);
			Future<GetReleaseStatusResponse> status = chartManager.getStatus(builder.build());
			GetReleaseStatusResponse getReleaseStatusResponse = status.get();
			Status status2 = getReleaseStatusResponse.getInfo().getStatus();
			Map<ResourcesType, String> readResources = null;
			if (hapi.release.StatusOuterClass.Status.Code.DEPLOYED == status2.getCode()) {
				readResources = ResourcesType.readResources(status2.getResources());
			} else {// 并没有部署成功时
				readResources = new HashMap<ResourcesType, String>();
				for (ResourcesType resourcesType : ResourcesType.values()) {
					readResources.put(resourcesType, status2.getCode().name());
				}
			}
			// chartManager.close();
			return readResources;
			/*
			 * String valueStr = readResources.get(ResourcesType.Pod); String colValue =
			 * ResourcesType.Pod.getColValue(valueStr, "STATUS");
			 * System.out.println(colValue); chartManager.close();
			 */
		} catch (Exception e) {
			if (!e.getMessage().contains("not found")) {// 没有布署正常，不打印错误了
				log.error("获取资源状态异常", e);
			}
			return new HashMap<>();
		}
	}

	// https://kubernetes-charts.storage.googleapis.com/redis-0.10.1.tgz
	public Result installUriChart(String name, String namespace, String uriStr, Object... keyValues) {
		Chart.Builder chartBuilder = null;
		try {
			final URI uri = URI.create(uriStr);
			final URL url = uri.toURL();
			final URLConnection connection = url.openConnection();
			Validate.notNull(connection);
			connection.addRequestProperty("Accept-Encoding", "gzip");
			connection.connect();
			Validate.isTrue("application/x-tar".equals(connection.getContentType()), "不支持的类型，需要.tgz结尾的压缩包");
			chartBuilder = urlLoader.load(url);
		} catch (Throwable e) {
			log.error("地址【" + uriStr + "】不能转为Chart，请检查正确的地址:", e);
			return Result.getError("地址【" + uriStr + "】不能转为Chart，请检查正确的地址:" + e.getMessage());
		}
		return this.installChart(name, namespace, chartBuilder, keyValues);
	}

}
